package mas;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

/** Class extends MetaAgent and adds the functionality to handle communication
 * over multiple virtual machines 
 * 
 * Advanced Java Programming - ICA 2 Middleware 
 * @author Carl Morphet, Matt Constable, Stephen Pammenter
 * Teesside University
 * Created: Apr 10, 2012
 */
public class SocketAgent extends MetaAgent
{   
    /** Instance variable of the portal linked to this SocketAgent */
    private Portal portal;
    
    /** The Java socket used to communication over VMs */
    private Socket socket;
    
    /** The input and output streams used */
    private PrintWriter output;
    private BufferedReader input;
    
    /** A boolean which controls the read loop */
    private boolean connected;
    
    /** Holds clients IP Address */
    private String ipAddress;
    
    /** The port number the socket uses */
    private final int portNo = 1234;

    /** Overloaded constructor which instantiates the SocketAgents instance 
     * variables 
     * 
     * @param name The SocketAgents identifier
     * @param portal The portal linked to this SocketAgent instance
     */
    public SocketAgent(String name, Portal portal)
    {
        super(name);
        portal.registerAgent(name, this);
        this.portal = portal;
        
        socket = null;
        output = null;
        input = null;
        
        try
        {
            ipAddress = InetAddress.getLocalHost().getHostName();
        }
        catch (UnknownHostException ex)
        {
            System.err.println("Error retrieving client IP: " + ex.getMessage());
        }
    }
    
    /** Method which instantiates the streams used for reading and writing */
    private void initialiseStreams()
    {
        try
        {
            socket = new Socket(ipAddress, portNo);
            output = new PrintWriter(socket.getOutputStream(), true);
            input = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        }
        catch(UnknownHostException hex)
        {
            System.err.println("Socket error: " + hex.getMessage());
        }
        catch(IOException iex)
        {
            System.err.println("Socket error: " + iex.getMessage());
        } 
        finally
        {
            connected = true;
        }
    }
    
    /** Method creates a connection in a new thread before calling the 
     * initialiseStreams() and startReadLoop() methods
     */
    protected void createConnection()
    {
        new Thread()
        {
            public void run()
            {
                try 
                {
                    ServerSocket server = new ServerSocket(portNo);
                    socket = server.accept();
                    
                    initialiseStreams();
                    startReadLoop();
                } 
                catch (IOException ex) 
                {
                    System.err.println("Socket error: " + ex.getMessage());
                }
            }
        }.start();
    }
    
    /** Method connects to an existing connection in a new thread before calling 
     * the initialiseStreams() and startReadLoop() methods 
     */
    protected void getConnection()
    {
        new Thread()
        {
            public void run()
            {
                try
                {
                    socket = new Socket(ipAddress, portNo);
                    
                    initialiseStreams();
                    startReadLoop();
                }
                catch(IOException ex)
                {
                    System.err.println("Socket error: " + ex.getMessage());
                }
            }
        }.start();
    }
    
    /** Method starts a read loop which streams and information coming in, 
     * packages it and adds it to the queue
     */
    private void startReadLoop()
    {
        String id, sender, recipient, content;
        
        while(connected)
        {
            BufferedReader inBR = new BufferedReader(new InputStreamReader(System.in));
            String userInput;

            try
            {
                while((userInput = inBR.readLine()) != null)
                {
                    int i = userInput.indexOf(" ");

                    id = userInput.substring(0, i);

                    sender = userInput.substring(i, userInput.indexOf(" ", ++i));

                    i = userInput.indexOf(" ", ++i);

                    recipient = userInput.substring(i, userInput.indexOf(" ", ++i));
                    i = userInput.indexOf(" ", ++i); 

                    content = userInput.substring(i, userInput.length());

                    portal.enqueue(new Message(id, sender, recipient, content));
                }
            } 
            catch (IOException ex)
            {
                System.err.println("Socket error :" + ex.getMessage());
            }
            finally
            {
                output.close();
                try
                {
                    input.close();
                    inBR.close();
                    socket.close();
                } 
                catch (IOException ex)
                {
                    System.err.println("Socket: Error closing streams: " + ex.getMessage());
                }
            }
        }
    }
}